#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <chrono>
#include <thread>
// #include <sw/redis++/redis++.h>
#include <hiredis/hiredis.h>
#include "Log.hpp"
using std::cout;
using std::endl;
using std::string;
using std::unordered_map;
using std::vector;
// using namespace sw::redis;

// RedisManager类
class RedisManager
{
private:
    RedisManager();
    ~RedisManager();
    RedisManager(const RedisManager &) = delete;
    RedisManager &operator=(const RedisManager &) = delete;

    void reconnect();

    redisContext *_context;
    const char *redisHost = "120.46.25.211";
    const int redisPort = 6379;

public:
    static RedisManager &GetInstance();

    bool InsertToken(const std::string &token, const std::string &username, int expireSeconds);
    std::string GetUserByToken(const std::string &token);
    bool DeleteToken(const std::string &token);
};

RedisManager &RedisManager::GetInstance()
{
    static RedisManager instance;
    return instance;
}

RedisManager::RedisManager()
    : _context(nullptr)
{
    reconnect();
}

void RedisManager::reconnect()
{
    if (_context)
        redisFree(_context);
    _context = redisConnect(redisHost, redisPort);
    if (_context == nullptr || _context->err)
    {
        //LOG(ERROR, "Redis connection error: %s", _context ? _context->errstr : "can't allocate context");
        _context = nullptr;
    }
}

RedisManager::~RedisManager()
{
    if (_context)
        redisFree(_context);
}

bool RedisManager::InsertToken(const std::string &token, const std::string &username, int expireSeconds)
{
    if (!_context)
        reconnect();
    if (!_context)
        return false;

    redisReply *reply = (redisReply *)redisCommand(_context,
                                                   "SETEX %s %d %s",
                                                   token.c_str(),
                                                   expireSeconds,
                                                   username.c_str());

    if (!reply)
    {
        reconnect();
        return false;
    }

    freeReplyObject(reply);
    return true;
}

std::string RedisManager::GetUserByToken(const std::string &token)
{
    if (!_context)
        reconnect();
    if (!_context)
        return "";

    redisReply *reply = (redisReply *)redisCommand(_context, "GET %s", token.c_str());
    if (!reply)
    {
        reconnect();
        return "";
    }

    std::string username;
    if (reply->type == REDIS_REPLY_STRING)
    {
        username = reply->str;
    }

    freeReplyObject(reply);
    return username;
}

bool RedisManager::DeleteToken(const std::string &token)
{
    if (!_context)
        reconnect();
    if (!_context)
        return false;

    redisReply *reply = (redisReply *)redisCommand(_context, "DEL %s", token.c_str());
    if (!reply)
    {
        reconnect();
        return false;
    }

    freeReplyObject(reply);
    return true;
}